{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": [],
      "authorship_tag": "ABX9TyOo4vuw8RG7Xi7iIdk+sJ78",
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/DanielWarfield1/MLWritingAndResearch/blob/main/AttentionDemo.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Attention\n",
        "in this notebook we will be implimenting an encoder-decoder style model for english to french translation. This model will employ a bi-directional lstm encoder and an attention based decoder as described in *Neural Machine Translation by Jointly Learning to Align and Translate*(2014).\n",
        "\n",
        "https://arxiv.org/pdf/1409.0473v7.pdf\n",
        "\n",
        "In sticking with the original paper, everything is set up to work with minibatches.\n",
        "\n",
        "Ref:\n",
        "\n",
        "https://github.com/bentrevett/pytorch-seq2seq/blob/master/3%20-%20Neural%20Machine%20Translation%20by%20Jointly%20Learning%20to%20Align%20and%20Translate.ipynb\n"
      ],
      "metadata": {
        "id": "fTXGtxYfFFoQ"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Downloading Dataset\n",
        "I saved the [kaggle english to french dataset](https://www.kaggle.com/datasets/dhruvildave/en-fr-translation-dataset?resource=download) on my drive for my own conveneince. You can store it in your google drive to run this notebook.\n",
        "\n",
        "This is zipped for compression purposes, and has to be unzipped to reveal the csv. This process takes a 4-5 minutes ish."
      ],
      "metadata": {
        "id": "BE4rvvFHGbfp"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#copying from google drive to local\n",
        "from google.colab import drive\n",
        "drive.mount('/content/drive')\n",
        "\n",
        "!cp '/content/drive/My Drive/ColabDatasets/en-fr.zip' 'en-fr.zip'"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "GynVJz_gKpX3",
        "outputId": "c15c0a6a-f338-45f5-83f1-c6252642d7d0"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Mounted at /content/drive\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "#unzipping en-fr\n",
        "!unzip en-fr.zip"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "GsmvjeK-G5iB",
        "outputId": "8136ceff-c93c-4703-c147-9dd65a276787"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Archive:  en-fr.zip\n",
            "  inflating: en-fr.csv               \n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Loading and Exploring Dataset\n",
        "There's a lot of data. As this notebook is chiefly for demonstrative purposes, I'll only be reading the first 500,000 english-french pairs."
      ],
      "metadata": {
        "id": "4NoZ6PU6RVCN"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "\"\"\"\n",
        "Loading subset of data that can fit into the memory of a modest computer.\n",
        "\"\"\"\n",
        "\n",
        "import pandas as pd\n",
        "\n",
        "#loading subset of the data and shuffling\n",
        "df_subset = pd.read_csv('en-fr.csv', nrows=500000).sample(frac = 1)\n",
        "\n",
        "#making sure the columns are defined as strings\n",
        "df_subset['en'] = df_subset.en.astype(str)\n",
        "df_subset['fr'] = df_subset.fr.astype(str)\n",
        "\n",
        "#displaying subset\n",
        "df_subset"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 424
        },
        "id": "1U2CdeTfRaoO",
        "outputId": "c9152091-e045-4723-9f47-3d3380d23f7f"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "                                                       en  \\\n",
              "141013  In fact, over the decade British Columbia lost...   \n",
              "401233  LIMITATIONS O. BMPs Management practices are a...   \n",
              "319259  This leads to poor vine performance, poor brix...   \n",
              "302312  In addition to tree fruit, berry crops, and ve...   \n",
              "281676  Renewal, the Environment, and Food Safety and ...   \n",
              "...                                                   ...   \n",
              "447440  **Continue if \"familiar\" with at least one pro...   \n",
              "43344   A strong local representative will have the kn...   \n",
              "31890   The market is particularly attractive, conside...   \n",
              "318831  Early planting and good weed management will m...   \n",
              "485691  likes to say that commercialization is not jus...   \n",
              "\n",
              "                                                       fr  \n",
              "141013  En fait, en dix ans, la Colombie-Britannique a...  \n",
              "401233  LIMITES DES MPG Les pratiques de gestion const...  \n",
              "319259  Il en résulte un piètre rendement de la vigne,...  \n",
              "302312  Outre la culture fruitière, des baies et des l...  \n",
              "281676  L’un des répondants estime que les revenus agr...  \n",
              "...                                                   ...  \n",
              "447440  Si votre interlocuteur a répondu à la question...  \n",
              "43344   Un représentant local compétent possédera les ...  \n",
              "31890   Ce marché suscite un intérêt particulier étant...  \n",
              "318831  Un ensemencement tôt en saison et une bonne ge...  \n",
              "485691  Mike Lazaridis aime répéter que la valorisatio...  \n",
              "\n",
              "[500000 rows x 2 columns]"
            ],
            "text/html": [
              "\n",
              "\n",
              "  <div id=\"df-1649839b-6263-4946-8f4c-686dc5d88977\">\n",
              "    <div class=\"colab-df-container\">\n",
              "      <div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>en</th>\n",
              "      <th>fr</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>141013</th>\n",
              "      <td>In fact, over the decade British Columbia lost...</td>\n",
              "      <td>En fait, en dix ans, la Colombie-Britannique a...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>401233</th>\n",
              "      <td>LIMITATIONS O. BMPs Management practices are a...</td>\n",
              "      <td>LIMITES DES MPG Les pratiques de gestion const...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>319259</th>\n",
              "      <td>This leads to poor vine performance, poor brix...</td>\n",
              "      <td>Il en résulte un piètre rendement de la vigne,...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>302312</th>\n",
              "      <td>In addition to tree fruit, berry crops, and ve...</td>\n",
              "      <td>Outre la culture fruitière, des baies et des l...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>281676</th>\n",
              "      <td>Renewal, the Environment, and Food Safety and ...</td>\n",
              "      <td>L’un des répondants estime que les revenus agr...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>...</th>\n",
              "      <td>...</td>\n",
              "      <td>...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>447440</th>\n",
              "      <td>**Continue if \"familiar\" with at least one pro...</td>\n",
              "      <td>Si votre interlocuteur a répondu à la question...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>43344</th>\n",
              "      <td>A strong local representative will have the kn...</td>\n",
              "      <td>Un représentant local compétent possédera les ...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>31890</th>\n",
              "      <td>The market is particularly attractive, conside...</td>\n",
              "      <td>Ce marché suscite un intérêt particulier étant...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>318831</th>\n",
              "      <td>Early planting and good weed management will m...</td>\n",
              "      <td>Un ensemencement tôt en saison et une bonne ge...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>485691</th>\n",
              "      <td>likes to say that commercialization is not jus...</td>\n",
              "      <td>Mike Lazaridis aime répéter que la valorisatio...</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "<p>500000 rows × 2 columns</p>\n",
              "</div>\n",
              "      <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-1649839b-6263-4946-8f4c-686dc5d88977')\"\n",
              "              title=\"Convert this dataframe to an interactive table.\"\n",
              "              style=\"display:none;\">\n",
              "\n",
              "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
              "       width=\"24px\">\n",
              "    <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
              "    <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
              "  </svg>\n",
              "      </button>\n",
              "\n",
              "\n",
              "\n",
              "    <div id=\"df-9ba1e3e9-4a28-401c-b6a8-50f1b32ef264\">\n",
              "      <button class=\"colab-df-quickchart\" onclick=\"quickchart('df-9ba1e3e9-4a28-401c-b6a8-50f1b32ef264')\"\n",
              "              title=\"Suggest charts.\"\n",
              "              style=\"display:none;\">\n",
              "\n",
              "<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
              "     width=\"24px\">\n",
              "    <g>\n",
              "        <path d=\"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z\"/>\n",
              "    </g>\n",
              "</svg>\n",
              "      </button>\n",
              "    </div>\n",
              "\n",
              "<style>\n",
              "  .colab-df-quickchart {\n",
              "    background-color: #E8F0FE;\n",
              "    border: none;\n",
              "    border-radius: 50%;\n",
              "    cursor: pointer;\n",
              "    display: none;\n",
              "    fill: #1967D2;\n",
              "    height: 32px;\n",
              "    padding: 0 0 0 0;\n",
              "    width: 32px;\n",
              "  }\n",
              "\n",
              "  .colab-df-quickchart:hover {\n",
              "    background-color: #E2EBFA;\n",
              "    box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "    fill: #174EA6;\n",
              "  }\n",
              "\n",
              "  [theme=dark] .colab-df-quickchart {\n",
              "    background-color: #3B4455;\n",
              "    fill: #D2E3FC;\n",
              "  }\n",
              "\n",
              "  [theme=dark] .colab-df-quickchart:hover {\n",
              "    background-color: #434B5C;\n",
              "    box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
              "    filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
              "    fill: #FFFFFF;\n",
              "  }\n",
              "</style>\n",
              "\n",
              "    <script>\n",
              "      async function quickchart(key) {\n",
              "        const containerElement = document.querySelector('#' + key);\n",
              "        const charts = await google.colab.kernel.invokeFunction(\n",
              "            'suggestCharts', [key], {});\n",
              "      }\n",
              "    </script>\n",
              "\n",
              "      <script>\n",
              "\n",
              "function displayQuickchartButton(domScope) {\n",
              "  let quickchartButtonEl =\n",
              "    domScope.querySelector('#df-9ba1e3e9-4a28-401c-b6a8-50f1b32ef264 button.colab-df-quickchart');\n",
              "  quickchartButtonEl.style.display =\n",
              "    google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "}\n",
              "\n",
              "        displayQuickchartButton(document);\n",
              "      </script>\n",
              "      <style>\n",
              "    .colab-df-container {\n",
              "      display:flex;\n",
              "      flex-wrap:wrap;\n",
              "      gap: 12px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert {\n",
              "      background-color: #E8F0FE;\n",
              "      border: none;\n",
              "      border-radius: 50%;\n",
              "      cursor: pointer;\n",
              "      display: none;\n",
              "      fill: #1967D2;\n",
              "      height: 32px;\n",
              "      padding: 0 0 0 0;\n",
              "      width: 32px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert:hover {\n",
              "      background-color: #E2EBFA;\n",
              "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "      fill: #174EA6;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert {\n",
              "      background-color: #3B4455;\n",
              "      fill: #D2E3FC;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert:hover {\n",
              "      background-color: #434B5C;\n",
              "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
              "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
              "      fill: #FFFFFF;\n",
              "    }\n",
              "  </style>\n",
              "\n",
              "      <script>\n",
              "        const buttonEl =\n",
              "          document.querySelector('#df-1649839b-6263-4946-8f4c-686dc5d88977 button.colab-df-convert');\n",
              "        buttonEl.style.display =\n",
              "          google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "\n",
              "        async function convertToInteractive(key) {\n",
              "          const element = document.querySelector('#df-1649839b-6263-4946-8f4c-686dc5d88977');\n",
              "          const dataTable =\n",
              "            await google.colab.kernel.invokeFunction('convertToInteractive',\n",
              "                                                     [key], {});\n",
              "          if (!dataTable) return;\n",
              "\n",
              "          const docLinkHtml = 'Like what you see? Visit the ' +\n",
              "            '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
              "            + ' to learn more about interactive tables.';\n",
              "          element.innerHTML = '';\n",
              "          dataTable['output_type'] = 'display_data';\n",
              "          await google.colab.output.renderOutput(dataTable, element);\n",
              "          const docLink = document.createElement('div');\n",
              "          docLink.innerHTML = docLinkHtml;\n",
              "          element.appendChild(docLink);\n",
              "        }\n",
              "      </script>\n",
              "    </div>\n",
              "  </div>\n"
            ]
          },
          "metadata": {},
          "execution_count": 3
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "\n",
        "\n",
        "\"\"\"\n",
        "Plotting how long phrases are in french and english, and how different\n",
        "corresponding lengths are\n",
        "\"\"\"\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "#computing sequence lengths\n",
        "df_subset['en_len'] = df_subset.en.str.split().apply(len)\n",
        "df_subset['fr_len'] = df_subset.fr.str.split().apply(len)\n",
        "\n",
        "#plotting histogram of length distribution, filtering out large outliers\n",
        "plt.hist(df_subset.en_len[df_subset.en_len<150], bins=100, alpha=0.5, label='english length')\n",
        "plt.hist(df_subset.fr_len[df_subset.fr_len<150], bins=100, alpha=0.5, label='french length')\n",
        "plt.legend()\n",
        "plt.show()\n",
        "\n",
        "#plotting how much longer french is by english, by percentage\n",
        "fr_per_larger = (df_subset.fr_len-df_subset.en_len)*100/ (df_subset.en_len)\n",
        "fr_per_larger = fr_per_larger[fr_per_larger<=200]\n",
        "plt.hist(fr_per_larger, bins=100, label='french percent larger than eng')\n",
        "plt.legend()\n",
        "plt.show()"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 843
        },
        "id": "KDZBu1isRhLo",
        "outputId": "05f694af-99f1-45a0-a6d3-e900aa70a90a"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjkAAAGdCAYAAADwjmIIAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA/TUlEQVR4nO3de1hVdd7+8RtRQFQgNEASFdM8o6mJu4NpomhMk5PTwRxDM310oBKePNA4aDpFWaaWJtM0pc1omR1sFEMRRcfEE0qeisosKt1YGezUBGWv3x/9WI9bUEE5yOL9uq51Xe61Pnvt72dLcvddJzfDMAwBAABYTL2aHgAAAEBVIOQAAABLIuQAAABLIuQAAABLIuQAAABLIuQAAABLIuQAAABLIuQAAABLql/TA6hJTqdTR44cUZMmTeTm5lbTwwEAAOVgGIZ++eUXBQcHq169C8/X1OmQc+TIEYWEhNT0MAAAwGX49ttv1aJFiwtur9Mhp0mTJpJ++5J8fHxqeDQAAKA8HA6HQkJCzN/jF1KnQ07JISofHx9CDgAAtcylTjXhxGMAAGBJhBwAAGBJhBwAAGBJdfqcHABA5TIMQ2fPnlVxcXFNDwW1mLu7u+rXr3/Ft3ch5AAAKkVRUZGOHj2qU6dO1fRQYAHe3t5q3ry5PDw8LnsfhBwAwBVzOp06fPiw3N3dFRwcLA8PD26yistiGIaKior0ww8/6PDhw2rXrt1Fb/h3MYQcAMAVKyoqktPpVEhIiLy9vWt6OKjlGjZsqAYNGuibb75RUVGRvLy8Lms/nHgMAKg0l/t/3MD5KuNniZ9GAABgSYQcAACqwahRozR06FDzdb9+/TRx4sRyvbcitRf6vJr09ddfy83NTdnZ2dX6uZyTAwCoUnPTPq/Wz4sbeEO1ft7lev/999WgQYOaHkalGzVqlPLz87Vy5cqaHgohBwCAmuDv71/TQ7A8DlcBAOo0p9OppKQkhYaGqmHDhurWrZveffddc3tGRobc3NyUnp6uXr16ydvbWzfffLNycnJc9vO3v/1NAQEBatKkiR555BFNnTpV3bt3v+Dnnn8I6pVXXlG7du3k5eWlwMBA/fGPfyw1zsmTJ8vf319BQUGaMWPGVdfnjBkztGTJEn344Ydyc3OTm5ubMjIyzPd+9dVX6t+/v7y9vdWtWzdlZmZWqIeKIuQAAOq0pKQkvfnmm0pOTtaBAwcUFxenP/3pT9q0aZNL3V/+8hfNmTNHu3btUv369fXwww+b25YuXaqnn35azz33nLKystSyZUstWrSo3GPYtWuXHnvsMc2cOVM5OTlKTU1V3759XWqWLFmiRo0aafv27Zo9e7ZmzpyptLS0q6rPJ554Qvfdd58GDx6so0eP6ujRo7r55ptd9v3EE08oOztbN9xwg4YPH66zZ8+Wu4eK4nAVTGUdN68tx7YB4HIUFhbqmWee0fr162Wz2SRJbdq00ZYtW/T3v/9dt99+u1n79NNPm6+nTp2qqKgonT59Wl5eXnr55Zc1ZswYjR49WpKUmJiodevW6cSJE+UaR25urho1aqTf/e53atKkiVq1aqUbb7zRpSYsLEzTp0+XJLVr104LFixQenq6Bg4ceNX02bhxYzVs2FCFhYUKCgoqNY4nnnhCUVFRkqSnnnpKnTt31pdffqkOHTqU63uqKGZyAAB11pdffqlTp05p4MCBaty4sbm8+eabOnTokEttWFiY+efmzZtLko4dOyZJysnJUe/evV3qz399MQMHDlSrVq3Upk0bjRw5UkuXLi31eIxzP79kDCWffylXS58X23dVYCYHAFBnlcxApKSk6LrrrnPZ5unp6fL63CuhSh5Z4XQ6K2UcTZo00e7du5WRkaF169YpMTFRM2bM0M6dO+Xn51fq80vGUN7Pv1r6rMp9l4WZHABAndWpUyd5enoqNzdXbdu2dVlCQkLKvZ/27dtr586dLuvOf30p9evXV0REhGbPnq29e/fq66+/1oYNGyq0jwupzj49PDyumqfQM5MDAKizmjRpoieeeEJxcXFyOp269dZbVVBQoI8//lg+Pj6Kjo4u134effRRjR07Vr169dLNN9+s5cuXa+/evWrTpk253r969Wp99dVX6tu3r6655hqtWbNGTqdT7du3v5L2TNXZZ+vWrbV27Vrl5OSoadOm8vX1rZQeLkeFZnIWLVqksLAw+fj4yMfHRzabTR999JG5vV+/fuYlYyXL+PHjXfaRm5urqKgoeXt7KyAgQJMmTSp1ZnVGRoZ69OghT09PtW3bVosXLy41loULF6p169by8vJSeHi4duzYUZFWAACQJM2aNUt//etflZSUpI4dO2rw4MFKSUlRaGhoufcxYsQIJSQk6IknnlCPHj10+PBhjRo1qtwPlvTz89P777+vO+64Qx07dlRycrLeeustde7c+XLbKqW6+hw7dqzat2+vXr166dprr9XHH39caT1UlJthGEZ5i1etWiV3d3e1a9dOhmFoyZIlev7557Vnzx517txZ/fr10w033KCZM2ea7/H29paPj48kqbi4WN27d1dQUJCef/55HT16VA899JDGjh2rZ555RpJ0+PBhdenSRePHj9cjjzyi9PR0TZw4USkpKYqMjJQkLV++XA899JCSk5MVHh6uefPmacWKFcrJyVFAQEC5m3c4HPL19VVBQYE5xrqMq6sAXK7Tp0/r8OHDCg0NvewnRlvNwIEDFRQUpH/96181PZQqVVV9Xuxnqry/vyt0uOquu+5yef30009r0aJF2rZtm5k2vb29y7xsTJLWrVungwcPav369QoMDFT37t01a9YsTZkyRTNmzJCHh4eSk5MVGhqqOXPmSJI6duyoLVu2aO7cuWbIefHFFzV27FjzErbk5GSlpKTo9ddf19SpUyvSEgAAV+zUqVNKTk5WZGSk3N3d9dZbb2n9+vUVuo9NbVDb+rzsE4+Li4v19ttv6+TJk+Y199JvNwpq1qyZunTpooSEBJdL4DIzM9W1a1cFBgaa6yIjI+VwOHTgwAGzJiIiwuWzIiMjzbsiFhUVKSsry6WmXr16ioiIuOSdEwsLC+VwOFwWAACulJubm9asWaO+ffuqZ8+eWrVqld57771Sv89qu9rWZ4VPPN63b59sNptOnz6txo0b64MPPlCnTp0kSQ8++KBatWql4OBg7d27V1OmTFFOTo7ef/99SZLdbncJOJLM13a7/aI1DodDv/76q37++WcVFxeXWfPZZ59ddOxJSUl66qmnKtoyAAAX1bBhQ61fv76mh1HlalufFQ457du3V3Z2tgoKCvTuu+8qOjpamzZtUqdOnTRu3DizrmvXrmrevLkGDBigQ4cO6frrr6/UgV+OhIQExcfHm68dDkeFLp0DAAC1R4VDjoeHh9q2bStJ6tmzp3bu3Kn58+fr73//e6na8PBwSb/dafH6669XUFBQqaug8vLyJMk8jycoKMhcd26Nj4+PGjZsKHd3d7m7u5dZc6FzgUp4enqWuukRAACwpiu+GaDT6VRhYWGZ27KzsyX9362bbTab9u3b53IL57S0NPn4+JiHvGw2m9LT0132k5aWZp734+HhoZ49e7rUOJ1Opaenu5wbBAAA6rYKzeQkJCRoyJAhatmypX755RctW7ZMGRkZWrt2rQ4dOqRly5bpzjvvVNOmTbV3717FxcWpb9++5rMqBg0apE6dOmnkyJGaPXu27Ha7pk2bppiYGHOGZfz48VqwYIEmT56shx9+WBs2bNA777yjlJQUcxzx8fGKjo5Wr1691Lt3b82bN08nT540r7YCAACoUMg5duyYHnroIR09elS+vr4KCwvT2rVrNXDgQH377bdav369GThCQkI0bNgwTZs2zXy/u7u7Vq9erQkTJshms6lRo0aKjo52ua9OaGioUlJSFBcXp/nz56tFixZ67bXXzMvHJen+++/XDz/8oMTERNntdnXv3l2pqamlTkZG5eNeOgCA2qJCNwO0Gm4G6Ko8AYaQA6As3AwQla0ybgbIAzrriLlpn5daAACSYRgaN26c/P395ebmZp5PWhMWL15sPnW8vDIyMuTm5qb8/PwqGVNF9evXTxMnTqzpYUjiAZ0AgKq2Mal6P69/QoXKU1NTtXjxYmVkZKhNmzZq1qxZFQ3MWjIyMtS/f3/9/PPPFQ5m1YWQAwCo0w4dOqTmzZvr5ptvvmBNUVGRPDw8qnFUqAwcrgIA1FmjRo3So48+qtzcXLm5ual169aSfjvkEhsbq4kTJ6pZs2bmxS/79+/XkCFD1LhxYwUGBmrkyJH68ccfzf3169dPjz32mCZPnix/f38FBQVpxowZLp+Zn5+v//mf/1FgYKC8vLzUpUsXrV692qVm7dq16tixoxo3bqzBgwfr6NGjFepry5Ytuu2229SwYUOFhIToscce08mTJ83trVu31jPPPKOHH35YTZo0UcuWLfXqq6+67GPr1q3q3r27vLy81KtXL61cudI8nPf111+rf//+kqRrrrlGbm5uGjVqlPlep9N50e+guhByAAB11vz58zVz5ky1aNFCR48e1c6dO81tS5YskYeHhz7++GMlJycrPz9fd9xxh2688Ubt2rVLqampysvL03333eeyzyVLlqhRo0bavn27Zs+erZkzZ5oPsHQ6nRoyZIg+/vhj/fvf/9bBgwf17LPPyt3d3Xz/qVOn9MILL+hf//qXNm/erNzcXD3xxBPl7unQoUMaPHiwhg0bpr1792r58uXasmWLYmNjXermzJmjXr16ac+ePfrzn/+sCRMmKCcnR9JvJ/bedddd6tq1q3bv3m0+TLtESEiI3nvvPUlSTk6Ojh49qvnz55frO6hOHK4CANRZvr6+atKkidzd3UvdNb9du3aaPXu2+fpvf/ubbrzxRj3zzDPmutdff10hISH6/PPPdcMNv11pGhYWpunTp5v7WLBggdLT0zVw4ECtX79eO3bs0KeffmrWt2nTxuVzz5w5o+TkZPNxSLGxsS63WrmUpKQkjRgxwjz5t127dnrppZd0++23a9GiReaVSnfeeaf+/Oc/S5KmTJmiuXPnauPGjWrfvr2WLVsmNzc3/eMf/5CXl5c6deqk77//XmPHjpX02y1h/P39JUkBAQGlzsm52HdQnQg5AACUoWfPni6vP/nkE23cuFGNGzcuVXvo0CGXkHOu5s2bm3f6z87OVosWLczasnh7e7s87/Hc95fHJ598or1792rp0qXmOsMw5HQ6dfjwYXXs2LHUON3c3BQUFGR+Tk5OjsLCwlwu3e7du3e5x3Cx76A6EXIAAChDo0aNXF6fOHFCd911l5577rlStSWPL5KkBg0auGxzc3OT0+mU9NtTvC+lrPdX5JZ2J06c0P/8z//oscceK7WtZcuW5RrnlarKfVcEIQcAgHLo0aOH3nvvPbVu3Vr161/er8+wsDB99913Loe3KluPHj108OBB82Hal6N9+/b697//rcLCQvOxS+eeryTJvNqsuLj48gdbxTjxGACAcoiJidHx48c1fPhw7dy5U4cOHdLatWs1evTocv+iv/3229W3b18NGzZMaWlpOnz4sD766COlpqZW2jinTJmirVu3KjY2VtnZ2friiy/04Ycfljrx+GIefPBBOZ1OjRs3Tp9++qnWrl2rF154QdJvszKS1KpVK7m5uWn16tX64YcfdOLEiUrrobIQcgAAKIfg4GB9/PHHKi4u1qBBg9S1a1dNnDhRfn5+qlev/L9O33vvPd10000aPny4OnXqpMmTJ1fqbEhYWJg2bdqkzz//XLfddptuvPFGJSYmKjg4uNz78PHx0apVq5Sdna3u3bvrL3/5ixITEyXJPE/nuuuu01NPPaWpU6cqMDCwQiGquvDsqjry7KrKei4Vz64CUBaeXWV9S5cu1ejRo1VQUFCuc4uuVGU8u4pzcgAAQClvvvmm2rRpo+uuu06ffPKJpkyZovvuu69aAk5lIeQAAIBS7Ha7EhMTZbfb1bx5c9177716+umna3pY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\n"
          },
          "metadata": {}
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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r9Iu1LYjR44oISFB/v7+io6O1rJlyxQVFaUFCxZc8LEvV6NCzt13362f//zn6tu3r5xOpz766COVlJTovffeu1Lta1IzZ85UaWmpuRw6dMjTTQLQzLzyyiuaM2eOOnfurCNHjmjr1q3mtnfffVc2m02bNm3SkiVLVFJSojvuuEM33nijtm3bpszMTBUXF+vBBx902+e7776rNm3aaMuWLZo7d67mzJmjrKwsST98eN59993atGmT/vKXv2jPnj1KS0uTj4+Pef9Tp07pxRdf1J///Gdt2LBBRUVFevbZZy96LFOnTtVLL72krVu3qmPHjrrnnnvMD6YDBw5o+PDhGjFihL744gutWLFCGzdu1IQJE9z28eKLL6pfv37asWOHfvOb3yg/P1933nmnYmJilJubq40bN+qee+4xr2admpqqP/3pT1qyZIl2796tyZMn67//+7+1fv16t/3++te/1ksvvaRt27apVatWeuKJJyRJI0eO1JQpU9zmh44cObJB/3bl5eVKTk7Wtm3blJ2dLW9vb91///2qqalxq5sxY4aeeeYZ7d27V06nU3/4wx+0dOlSvfPOO9q0aZPKysr0wQcfuN2nocc1Y8YMpaWlae/everbt2+dNtaeRLNmzRodOXJEf//7381t69at04EDB7Ru3TpzTuvZAa+qqkovvPCCPv/8c33wwQc6ePCgHnvssTqPcb6+rU9VVZWcTqcCAwP1//7f/9OmTZvMIH32yN3F2vboo4/q8OHDysnJ0d/+9je9+eabV2Ue7WVdJyc4OFjXX3+99u/fr7vuukuVlZUqKSlxG80pLi425/CEh4fXOQuq9uyrs2vOPSOruLhYdrtd/v7+8vHxkY+PT7019c0VOpuvr698fX0v6VgBTzt3nkpj55GgaQQFBSkwMFA+Pj513nOuu+46zZ0717z929/+VjfeeKN+//vfm+vefvttRUZG6p///Keuv/56SVLfvn01a9Yscx8LFy5Udna27rrrLq1Zs0afffaZ9u7da9Z369bN7XGrqqq0ZMkS/fjHP5YkTZgwQXPmzLnoscyaNUt33XWXpB+CVufOnfX+++/rwQcfVGpqqkaPHq1JkyaZ7Xr11Vd1++23a/HixeZ1S+644w5NmTLF3OfDDz+sgQMH6vXXXzfX3XDDDZJ+mDLw+9//XmvWrJHD4TCPZePGjXrjjTd0++23m/f53e9+Z96eMWOGEhISdPr0afn7+6tt27bnnR96ISNGjHC7/fbbb6tjx47as2ePevfuba6fNGmS20kzr732mmbOnKn7779fkrRw4UJ99NFH5vbGHNecOXPMPq9Px44dJUnt27evc3zt2rXTwoUL5ePjo549eyohIUHZ2dl66qmnJMktrHTr1k2vvvqqbrrpJp08eVJt27Y1t52vb+u7Fs2KFStUU1Ojt956y7xQ5zvvvKPg4GDl5ORo2LBhF23bvn37tGbNGm3dulUDBw6UJL311lu67rrrztsPTeWyTkI/efKk+d10bGysWrdurezsbHN7QUGBioqKzH90h8OhnTt3uqW3rKws2e12xcTEmDVn76O2pnYfNptNsbGxbjU1NTXKzs42awDAE2JjY91uf/7551q3bp3atm1rLrWX3Tj7a5Jz/6Lv1KmT+T6Zn5+vzp07mwGnPgEBAWbAOff+F3L2e2ZISIh69OihvXv3mm1PT093a7vT6TSvUl2r9kOrVu1ITn3279+vU6dO6a677nLb75/+9Ce3/pDc+6RTp06SdNl/+X/55ZcaNWqUunXrJrvdbn7VWDu3tL5jKi0tVXFxsQYNGmSu8/Hxcfu3bsxxndtfjXHDDTe4jeCd+++cl5ene+65R126dFFgYKAZZM49vsb07eeff679+/crMDDQPK6QkBCdPn3a7dgu1LaCggK1atVKAwYMMLd3795d7dq1a3QfNFajRnKeffZZ3XPPPeratasOHz6sWbNmycfHR6NGjVJQUJDGjBmj5ORkhYSEyG63a+LEiXI4HBo8eLAkadiwYYqJidEjjzyiuXPnyuVy6bnnnlNSUpI5wjJu3DgtXLhQ06ZN0xNPPKG1a9fqvffeU0bGf/6KTU5OVmJiogYOHKhBgwZpwYIFKi8vN8+2AgBPaNOmjdvtkydP6p577tEf/vCHOrW1Hy6S1Lp1a7dtXl5e5lco/v7+F33c+u5vGEaD212fkydP6he/+IWefvrpOtu6dOli/v+5x3yh9p48eVKSlJGRoR/96Edu284dZT/7mGpHEM79Wqmxaj+//vjHPyoiIkI1NTXq3bt3nQnT5x7TxTTmuBq777Nd6HlSXl4up9Mpp9OppUuXqmPHjioqKpLT6axzfI3p25MnTyo2NlZLly6ts6121OlibfOkRoWcr7/+WqNGjdK3336rjh076pZbbtGnn35qHuj8+fPl7e2tESNGqKKiQk6n023I0sfHR6tWrdL48ePlcDjUpk0bJSYmug2rRkdHKyMjQ5MnT9Yrr7yizp0766233jIn8Uk/fCd77NgxpaSkyOVyqX///srMzKwzGRkAPGnAgAH629/+pqioqHrPwGqIvn376uuvv3b7equpfPrpp2ZgOX78uP75z3+qV69ekn5o+549e9S9e/dGtzc7O1vPP/98nW0xMTHy9fVVUVGR21c4jWWz2Rr9i/XffvutCgoK9Mc//lG33nqrJNU78fdcQUFBCgsL09atW3XbbbdJ+uGab9u3bzdP8W6q45Jk/hhlY49v3759+vbbb5WWlmaeVLNt27bLaov0w/NgxYoVCg0Nld1uv6R99OjRQ2fOnNGOHTvMEbD9+/ebk9GvpEa96pYvX37B7X5+flq0aJEWLVp03pquXbu6fZdZnyFDhmjHjh0XrJkwYUKdCXAAcC1JSkrSH//4R40aNco8e2r//v1avny53nrrLbfh/fO5/fbbddttt2nEiBF6+eWX1b17d+3bt09eXl4aPnz4ZbVvzpw5at++vcLCwvTrX/9aHTp0MM8Ymj59ugYPHqwJEyboySefVJs2bbRnzx5lZWVp4cKF593nzJkz1adPH/3yl7/UuHHjZLPZtG7dOv385z9Xhw4d9Oyzz2ry5MmqqanRLbfcotLSUm3atEl2u12JiYkNandUVJQKCwvNr/ICAwMvOt+yXbt2at++vd5880116tRJRUVFDb6u2sSJE5Wamqru3burZ8+eeu2113T8+HFzFCQwMLBJjkuSQkND5e/vr8zMTHXu3Fl+fn4KCgq66P26dOkim82m1157TePGjdOuXbv0wgsvNPhxz2f06NGaN2+e7r33XnPC/VdffaW///3vmjZtmjp37nzRffTs2VPx8fEaO3asFi9erNatW2vKlCny9/e/4j/Iyw/GAMAVEhERoU2bNqm6ulrDhg1Tnz59NGnSJAUHBzfqd3n+9re/6aabbtKoUaMUExOjadOmNfov/fqkpaXpmWeeUWxsrFwulz788ENzJKFv375av369/vnPf+rWW2/VjTfeqJSUFEVERFxwn9dff70++eQTff755xo0aJAcDof+8Y9/mCNZL7zwgn7zm98oNTVVvXr10vDhw5WRkWGeOt0QI0aM0PDhwzV06FB1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qfXr1+vw4cN64IEHPNjaq6e8vFz9+vXTokWL6t0+d+5cvfrqq1qyZIm2bNmiNm3ayOl06vTp02bN6NGjtXv3bmVlZWnVqlXasGGDxo4de7UO4aq5WF9J0vDhw92eZ3/961/dtreUvlq/fr2SkpL06aefKisrS1VVVRo2bJjKy8vNmou97hryudhkmuTXMmG88847RlBQUJ31H330keHt7W24XC5z3eLFiw273W5UVFQYhmEY06ZNM2644Qa3+40cOdJwOp1XtM2e1rVrV2P+/Pnn3f76668b7dq1M/vJMAxj+vTpRo8ePa5C664tgwYNMpKSkszb1dXVRkREhJGamurBVnnerFmzjH79+tW7raSkxGjdurWxcuVKc93evXsNSUZubu5VauG1QZLx/vvvm7dramqM8PBwY968eea6kpISw9fX1/jrX/9qGIZh7Nmzx5BkbN261az5+OOPDS8vL+Pf//73VWv71XZuXxmGYSQmJhr33nvvee/TUvvKMAzj6NGjhiRj/fr1hmE07HXXkM/FpsJIzhWWm5urPn36uF2g0Ol0qqysTLt37zZr4uPj3e7ndDqVm5t7VdvqCWlpaWrfvr1uvPFGzZs3z224Mjc3V7fddptsNpu5zul0qqCgQMePH/dEcz2isrJSeXl5bs8Rb29vxcfHt4jnyMV8+eWXioiIULdu3TR69GgVFRVJkvLy8lRVVeXWbz179lSXLl1afL8VFhbK5XK59U1QUJDi4uLMvsnNzVVwcLAGDhxo1sTHx8vb21tbtmy56m32tJycHIWGhqpHjx4aP368vv32W3NbS+6r0tJSSVJISIikhr3uGvK52FSa9RWPmwOXy1XnCsy1t10u1wVrysrK9P3338vf3//qNPYqe/rppzVgwACFhIRo8+bNmjlzpo4cOaKXX35Z0g/9Eh0d7Xafs/uuXbt2V73NnvDNN9+ourq63ufIvn37PNSqa0NcXJzS09PVo0cPHTlyRM8//7xuvfVW7dq1Sy6XSzabrc5cubCwMPO111LVHn99z6mz35dCQ0Pdtrdq1UohISEtrv+GDx+uBx54QNHR0Tpw4IB+9atf6e6771Zubq58fHxabF/V1NRo0qRJ+slPfqLevXtLUoNedw35XGwqhJx6zJgxQ3/4wx8uWLN37163CY74QWP6Ljk52VzXt29f2Ww2/eIXv1BqaiqXTEeD3H333eb/9+3bV3Fxceratavee+89y/5xgKvvoYceMv+/T58+6tu3r3784x8rJydHd955pwdb5llJSUnatWuX2zy4aw0hpx5TpkzRY489dsGabt26NWhf4eHhdc6CqZ1lHh4ebv733JnnxcXFstvtze6N+nL6Li4uTmfOnNHBgwfVo0eP8/aL9J++awk6dOggHx+fevuiJfVDQwQHB+v666/X/v37ddddd6myslIlJSVuf1XSb/95/RQXF6tTp07m+uLiYvXv39+sOXdi+5kzZ/Tdd9+1+P7r1q2bOnTooP379+vOO+9skX01YcIEc4J1586dzfXh4eEXfd015HOxqTAnpx4dO3ZUz549L7icPU/kQhwOh3bu3On2AsjKypLdbldMTIxZk52d7Xa/rKwsORyOpjuoq+Ry+i4/P1/e3t7msK/D4dCGDRtUVVVl1mRlZalHjx4t5qsqSbLZbIqNjXV7jtTU1Cg7O7tZPkeupJMnT+rAgQPq1KmTYmNj1bp1a7d+KygoUFFRUYvvt+joaIWHh7v1TVlZmbZs2WL2jcPhUElJifLy8syatWvXqqamRnFxcVe9zdeSr7/+Wt9++60ZEFtSXxmGoQkTJuj999/X2rVr60wpaMjrriGfi03ZYFyGr776ytixY4fx/PPPG23btjV27Nhh7Nixwzhx4oRhGIZx5swZo3fv3sawYcOM/Px8IzMz0+jYsaMxc+ZMcx//+te/jICAAGPq1KnG3r17jUWLFhk+Pj5GZmampw7ritu8ebMxf/58Iz8/3zhw4IDxl7/8xejYsaPx6KOPmjUlJSVGWFiY8cgjjxi7du0yli9fbgQEBBhvvPGGB1vuGcuXLzd8fX2N9PR0Y8+ePcbYsWON4OBgt7MTWqIpU6YYOTk5RmFhobFp0yYjPj7e6NChg3H06FHDMAxj3LhxRpcuXYy1a9ca27ZtMxwOh+FwODzc6qvjxIkT5vuRJOPll182duzYYXz11VeGYRhGWlqaERwcbPzjH/8wvvjiC+Pee+81oqOjje+//97cx/Dhw40bb7zR2LJli7Fx40bjuuuuM0aNGuWpQ7piLtRXJ06cMJ599lkjNzfXKCwsNNasWWMMGDDAuO6664zTp0+b+2gpfTV+/HgjKCjIyMnJMY4cOWIup06dMmsu9rpryOdiUyHkXKbExERDUp1l3bp1Zs3BgweNu+++2/D39zc6dOhgTJkyxaiqqnLbz7p164z+/fsbNpvN6Natm/HOO+9c3QO5yvLy8oy4uDgjKCjI8PPzM3r16mX8/ve/d3vTMAzD+Pzzz41bbrnF8PX1NX70ox8ZaWlpHmqx57322mtGly5dDJvNZgwaNMj49NNPPd0kjxs5cqTRqVMnw2azGT/60Y+MkSNHGvv37ze3f//998Yvf/lLo127dkZAQIBx//33G0eOHPFgi6+edevW1fvelJiYaBjGD6eR/+Y3vzHCwsIMX19f48477zQKCgrc9vHtt98ao0aNMtq2bWvY7Xbj8ccfN/+As5IL9dWpU6eMYcOGGR07djRat25tdO3a1Xjqqafq/IHRUvqqvn6S5PaZ1ZDXXUM+F5uC1//faAAAAEthTg4AALAkQg4AALAkQg4AALAkQg4AALAkQg4AALAkQg4AALAkQg4AALAkQg4AALAkQg4AALAkQg4AALAkQg4AALAkQg4AALCk/w9zOUmNP93KTgAAAABJRU5ErkJggg==\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "\"\"\"\n",
        "Printing a random sample, for sanity checking purposes.\n",
        "\"\"\"\n",
        "\n",
        "#for wrapping text that is too ling\n",
        "import textwrap\n",
        "\n",
        "samp = df_subset.sample(1).iloc[0]\n",
        "samp\n",
        "print('================== English: {} words ================='.format(samp.en_len))\n",
        "print(textwrap.fill(samp.en, 100))\n",
        "print('================== French: {} words ================='.format(samp.fr_len))\n",
        "print(textwrap.fill(samp.fr, 100))"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "YKxef1SUTvz1",
        "outputId": "bcdb0e4c-bd98-40e2-d485-e6783ed09b9a"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "================== English: 18 words =================\n",
            "Termination This Implementation Agreement, or parts thereof, may be terminated in writing by any\n",
            "mutually agreed upon date.\n",
            "================== French: 24 words =================\n",
            "Il est entendu que l=exercice des attributions par le comité de gestion en vertu du paragraphe 3.4\n",
            "des présentes ne constitue pas une modification.\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Creating a text embeder\n",
        "using a word2vect style word embeder for both english and french.\n",
        "\n",
        "In this example I'm using pre-trained word-piece embeddings from https://github.com/bheinzerling/bpemb."
      ],
      "metadata": {
        "id": "l38rs6Y8dKHt"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "!pip install bpemb"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "lWArNyOHadk_",
        "outputId": "73eb48ab-d8e3-4ffb-edd7-fda1660c81d9"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Collecting bpemb\n",
            "  Downloading bpemb-0.3.4-py3-none-any.whl (19 kB)\n",
            "Requirement already satisfied: gensim in /usr/local/lib/python3.10/dist-packages (from bpemb) (4.3.1)\n",
            "Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from bpemb) (1.22.4)\n",
            "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from bpemb) (2.27.1)\n",
            "Collecting sentencepiece (from bpemb)\n",
            "  Downloading sentencepiece-0.1.99-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB)\n",
            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.3/1.3 MB\u001b[0m \u001b[31m17.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hRequirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from bpemb) (4.65.0)\n",
            "Requirement already satisfied: scipy>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from gensim->bpemb) (1.10.1)\n",
            "Requirement already satisfied: smart-open>=1.8.1 in /usr/local/lib/python3.10/dist-packages (from gensim->bpemb) (6.3.0)\n",
            "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests->bpemb) (1.26.16)\n",
            "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests->bpemb) (2023.5.7)\n",
            "Requirement already satisfied: charset-normalizer~=2.0.0 in /usr/local/lib/python3.10/dist-packages (from requests->bpemb) (2.0.12)\n",
            "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests->bpemb) (3.4)\n",
            "Installing collected packages: sentencepiece, bpemb\n",
            "Successfully installed bpemb-0.3.4 sentencepiece-0.1.99\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "\"\"\"\n",
        "subword embeding experimentation\n",
        "\n",
        "uses BPEmb, which is a subword embeder compatible with multiple languages.\n",
        "This module loads a pre-trained word embeding, breaks words into sub-words,\n",
        "places them in an embeding vector, and can convert embeding vectors to sub-words\n",
        "based on similarity.\n",
        "\n",
        "Because there's no out-of-the-box support for utility tokens (<start> and <stop>)\n",
        "I'm planning on using a vector of all zeros to denote non-output. This is important\n",
        "as I have to pad jagged matrixies when constructing mini-batches, and also decide\n",
        "when to stop outputting from the model when inferencing.\n",
        "\n",
        "It's important that this vector can't be confused as a real token, so I'm\n",
        "experimenting to see if cosin-similarity is used as the similarity function,\n",
        "which is magnitude invarient. If the embedding is magnitude invarient, I can use\n",
        "the magnitude of the vector to decide weather to otuput something or not.\n",
        "\n",
        "An addition to this might be adding another dimension as an \"exists or not\"\n",
        "dimension, this could be done both on the input and output.\n",
        "\"\"\"\n",
        "\n",
        "from bpemb import BPEmb\n",
        "import numpy as np\n",
        "\n",
        "#embedding vector length\n",
        "EMBED_DIM = 50\n",
        "\n",
        "print('=======================================================')\n",
        "print('||                      English                      ||')\n",
        "print('=======================================================')\n",
        "\n",
        "bpemb_en = BPEmb(lang=\"en\", dim=EMBED_DIM)\n",
        "\n",
        "txt_en = \"How does the janitor misenterperate the signage?\"\n",
        "\n",
        "print('example:')             #printing the example\n",
        "print(txt_en)\n",
        "print('\\nsub-word tokens:')   #printing the sub-word tokens\n",
        "print(bpemb_en.encode(txt_en))\n",
        "\n",
        "for i in range(2):\n",
        "    print('\\n=========== sub-word {} ============='.format(i))\n",
        "    print('subword \"{}\":'.format(bpemb_en.encode(txt_en)[i]))\n",
        "    print('embeding (first 5 dimensions):')\n",
        "    print(bpemb_en.embed(txt_en)[i][:5])\n",
        "    print('embeding similarity (top 3):')\n",
        "    print(bpemb_en.most_similar(bpemb_en.embed(txt_en)[i])[:3])\n",
        "\n",
        "\n",
        "print('\\n\\n=======================================================')\n",
        "print('||                      French                       ||')\n",
        "print('=======================================================')\n",
        "\n",
        "bpemb_fr = BPEmb(lang=\"fr\", dim=EMBED_DIM)\n",
        "\n",
        "txt_fr = \"Comment le concierge interprète-t-il mal la signalisation?\"\n",
        "\n",
        "print('example:')             #printing the example\n",
        "print(txt_fr)\n",
        "print('\\nsub-word tokens:')   #printing the sub-word tokens\n",
        "print(bpemb_fr.encode(txt_fr))\n",
        "\n",
        "for i in range(2):\n",
        "    print('\\n=========== sub-word {} ============='.format(i))\n",
        "    print('subword \"{}\":'.format(bpemb_fr.encode(txt_fr)[i]))\n",
        "    print('embeding (first 5 dimensions):')\n",
        "    print(bpemb_fr.embed(txt_fr)[i][:5])\n",
        "    print('embeding similarity (top 3):')\n",
        "    print(bpemb_fr.most_similar(bpemb_fr.embed(txt_fr)[i])[:3])\n",
        "\n",
        "\n",
        "print('\\n\\n=======================================================')\n",
        "print('||                      Utility                       ||')\n",
        "print('=======================================================')\n",
        "all1 = np.ones((EMBED_DIM)) * 0.001\n",
        "print('similarities for magnitude 1')\n",
        "print(bpemb_fr.most_similar(all1)[:3])\n",
        "print(bpemb_en.most_similar(all1)[:3])\n",
        "all1 = np.ones((EMBED_DIM)) * 0.00001\n",
        "print('similarities for magnitude 2, same direction')\n",
        "print(bpemb_fr.most_similar(all1)[:3])\n",
        "print(bpemb_en.most_similar(all1)[:3])\n",
        "print('\\nsimilarities are magnitude invarient')"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "XXeblaRlqYp6",
        "outputId": "1d5881cc-9600-4e9f-b654-fbe644159594"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "=======================================================\n",
            "||                      English                      ||\n",
            "=======================================================\n",
            "downloading https://nlp.h-its.org/bpemb/en/en.wiki.bpe.vs10000.model\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "100%|██████████| 400869/400869 [00:00<00:00, 1133908.58B/s]\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "downloading https://nlp.h-its.org/bpemb/en/en.wiki.bpe.vs10000.d50.w2v.bin.tar.gz\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "100%|██████████| 1924908/1924908 [00:00<00:00, 3171997.10B/s]\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "example:\n",
            "How does the janitor misenterperate the signage?\n",
            "\n",
            "sub-word tokens:\n",
            "['▁how', '▁does', '▁the', '▁jan', 'itor', '▁mis', 'ent', 'er', 'per', 'ate', '▁the', '▁sign', 'age', '?']\n",
            "\n",
            "=========== sub-word 0 =============\n",
            "subword \"▁how\":\n",
            "embeding (first 5 dimensions):\n",
            "[ 0.134556  0.25324  -0.300284 -0.050174  0.225998]\n",
            "embeding similarity (top 3):\n",
            "[('▁how', 1.0), ('▁why', 0.8465365767478943), ('▁understand', 0.8464795351028442)]\n",
            "\n",
            "=========== sub-word 1 =============\n",
            "subword \"▁does\":\n",
            "embeding (first 5 dimensions):\n",
            "[ 0.371606  0.411344  0.312751 -0.025828  0.197103]\n",
            "embeding similarity (top 3):\n",
            "[('▁does', 1.0000001192092896), ('▁makes', 0.7987446784973145), ('▁whether', 0.7980255484580994)]\n",
            "\n",
            "\n",
            "=======================================================\n",
            "||                      French                       ||\n",
            "=======================================================\n",
            "downloading https://nlp.h-its.org/bpemb/fr/fr.wiki.bpe.vs10000.model\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "100%|██████████| 400824/400824 [00:00<00:00, 957749.38B/s]\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "downloading https://nlp.h-its.org/bpemb/fr/fr.wiki.bpe.vs10000.d50.w2v.bin.tar.gz\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "100%|██████████| 1924882/1924882 [00:00<00:00, 3221262.45B/s]\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "example:\n",
            "Comment le concierge interprète-t-il mal la signalisation?\n",
            "\n",
            "sub-word tokens:\n",
            "['▁comment', '▁le', '▁con', 'cier', 'ge', '▁interprète', '-', 't', '-', 'il', '▁mal', '▁la', '▁sign', 'alisation', '?']\n",
            "\n",
            "=========== sub-word 0 =============\n",
            "subword \"▁comment\":\n",
            "embeding (first 5 dimensions):\n",
            "[-0.242793  0.644877  0.446036 -0.111873 -0.693018]\n",
            "embeding similarity (top 3):\n",
            "[('▁comment', 1.0), ('▁lire', 0.7813255190849304), ('▁nous', 0.7470166087150574)]\n",
            "\n",
            "=========== sub-word 1 =============\n",
            "subword \"▁le\":\n",
            "embeding (first 5 dimensions):\n",
            "[-0.09845  -0.11444   0.33016   0.275052  0.237902]\n",
            "embeding similarity (top 3):\n",
            "[('▁le', 1.0), ('▁premier', 0.8239309787750244), ('▁dernier', 0.8175572156906128)]\n",
            "\n",
            "\n",
            "=======================================================\n",
            "||                      Utility                       ||\n",
            "=======================================================\n",
            "similarities for magnitude 1\n",
            "[('▁nuclé', 0.4147549569606781), ('sol', 0.4073708951473236), ('nik', 0.40434518456459045)]\n",
            "[('▁pam', 0.4569125473499298), ('enda', 0.431844025850296), ('uin', 0.4181838929653168)]\n",
            "similarities for magnitude 2, same direction\n",
            "[('▁nuclé', 0.4147549569606781), ('sol', 0.4073708951473236), ('nik', 0.40434518456459045)]\n",
            "[('▁pam', 0.4569125473499298), ('enda', 0.431844025850296), ('uin', 0.4181838929653168)]\n",
            "\n",
            "similarities are magnitude invarient\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "\"\"\"\n",
        "building a function for embeding an input-output pair,\n",
        "based on the previous experiments.\n",
        "\n",
        "Because this will be creating the i/o for the model, this has to accept batches\n",
        "of english and french pairs.\n",
        "\"\"\"\n",
        "import torch\n",
        "\n",
        "#for padding 0s\n",
        "def boolean_indexing(v):\n",
        "    lens = np.array([len(item) for item in v])\n",
        "    print('----')\n",
        "    print(lens)\n",
        "    mask = lens[:,None] > np.arange(lens.max())\n",
        "    mask = np.expand_dims(mask,2)\n",
        "    mask = np.repeat(mask, EMBED_DIM, 2)\n",
        "    out = np.zeros((mask.shape[0], mask.shape[1], EMBED_DIM),dtype=int)\n",
        "    print('----')\n",
        "    print(out.shape)\n",
        "    print(mask.shape)\n",
        "    print(v.shape)\n",
        "    print(v[0].shape)\n",
        "    return out\n",
        "\n",
        "def xy_embeding(batch_en, batch_fr):\n",
        "    \"\"\"\n",
        "    takes in a batch of english sentences, and a corresponding batch of\n",
        "    french sentences. Embeds each sentence in the batch, then constructs\n",
        "    a padded tensor containing the embeddings. Outputs a pytorch tensor\n",
        "    \"\"\"\n",
        "\n",
        "    #embedding all sentences\n",
        "    emb_en = [bpemb_en.embed(txt_en) for txt_en in batch_en]\n",
        "    emb_fr = [bpemb_en.embed(txt_en) for txt_en in batch_fr]\n",
        "\n",
        "    #computing the maximum length\n",
        "    emb_en_maxlen = max([len(e) for e in emb_en])\n",
        "    emb_fr_maxlen = max([len(e) for e in emb_fr])\n",
        "\n",
        "    #constructing a placeholder\n",
        "    emb_en_out = np.zeros((len(emb_en), emb_en_maxlen, EMBED_DIM), dtype=np.float32)\n",
        "    emb_fr_out = np.zeros((len(emb_en), emb_fr_maxlen, EMBED_DIM), dtype=np.float32)\n",
        "\n",
        "    #filling in the actual embeddings, both have same batch size\n",
        "    for i in range(len(batch_en)):\n",
        "        emb_en_out[i,:emb_en[i].shape[0],:] = emb_en[i]\n",
        "        emb_fr_out[i,:emb_fr[i].shape[0],:] = emb_fr[i]\n",
        "\n",
        "    return torch.from_numpy(emb_en_out), torch.from_numpy(emb_fr_out)\n",
        "\n",
        "#creating a batch of embeddings\n",
        "#including both language in one to create a jagged array for testing.\n",
        "#in reality these would be several english and several french examples\n",
        "#respectively. These are not numpy arrays because they are jagged.\n",
        "batch_en = [\"hello to you\", \"without a doubt he did?\", \"expeditiously executed directive is done\"]\n",
        "batch_fr = [\"Bonjour à vous\", \"sans aucun doute il l'a fait?\", \"directive exécutée rapidement est faite\"]\n",
        "\n",
        "emb_en, emb_fr = xy_embeding(batch_en, batch_fr)\n",
        "print('test english batched embeddings: {}'.format(emb_en.shape))\n",
        "print('test french batched embeddings: {}'.format(emb_fr.shape))"
      ],
      "metadata": {
        "id": "qa86smwqs4iE",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "c1f004bb-bc03-4098-b77f-42a7612a3671"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "test english batched embeddings: torch.Size([3, 9, 50])\n",
            "test french batched embeddings: torch.Size([3, 15, 50])\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Creating an attention mechanism\n",
        "Implimenting the attention mechanism from https://arxiv.org/pdf/1409.0473v7.pdf, from scratch.\n",
        "\n",
        " - define alignment function with learnable parameters\n",
        " - define that function in such a way where it accepts\n",
        "    - the previous decoder embedding\n",
        "    - all encoder embeddings\n",
        " - Put that into a pytorch module with learnable parameters"
      ],
      "metadata": {
        "id": "ibL5OPky4Yx0"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "\"\"\"\n",
        "Implimenting the alignment function, which has trainable parameters v, W, and U.\n",
        "the whole idea is that, given an embedding for the encoder and decoder, the\n",
        "alignment func outputs a scalar raiting the alignment. So, the shapes of\n",
        "v, W, and U should be such that the output is a scalar.\n",
        "\"\"\"\n",
        "\n",
        "import torch\n",
        "import torch.nn.functional as F\n",
        "\n",
        "#these need to be sized in such a way that matrix multiplication yields a scalar\n",
        "#otherwise, they're just general learnable parameters. Different alignment\n",
        "#functions might have different parameters. For instance, \"attention is all you\n",
        "#need\" uses a projection head that generates a query, key, and value, which are\n",
        "#used in a different self-alignment function. this can allign vectors of different\n",
        "#lengths\n",
        "encoder_embedding_dim = EMBED_DIM*2\n",
        "decoder_embedding_dim = EMBED_DIM\n",
        "\n",
        "U_attention = torch.rand(EMBED_DIM, encoder_embedding_dim)\n",
        "W_attention = torch.rand(decoder_embedding_dim, EMBED_DIM)\n",
        "v_attention = torch.rand(1,EMBED_DIM)\n",
        "\n",
        "def alignment_func(s, h, W=W_attention, U=U_attention, v=v_attention):\n",
        "    \"\"\"\n",
        "    s: si-1, from the paper, the previous decoder state\n",
        "    h: hj, from the paper, an input embedding\n",
        "    W,U,v: trainable parameters\n",
        "\n",
        "    calculates v*tanh(W*s + U*h), should return the scalar alpha\n",
        "    \"\"\"\n",
        "\n",
        "    v1 = torch.matmul(W,s)\n",
        "    v2 = torch.matmul(U,h)\n",
        "    v3 = F.tanh(v1+v2)\n",
        "\n",
        "    return torch.matmul(v, v3)\n"
      ],
      "metadata": {
        "id": "ExIFepJO2roB"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "#testing the alignment function between one embedded word and another\n",
        "#dividing by zero to get them in a good range for tanh\n",
        "s = torch.rand(decoder_embedding_dim)/50\n",
        "h = torch.rand(encoder_embedding_dim)/50\n",
        "alignment_func(s, h)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "3MNpabyd56Op",
        "outputId": "353a4a99-045f-4790-b8ed-2f1bc27adc38"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "tensor([16.2514])"
            ]
          },
          "metadata": {},
          "execution_count": 10
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "\"\"\"\n",
        "defining attention, wich is a list of softmaxed alignment scores for all input\n",
        "embeddings (hj) given the previous decoder embedding (si-1). This is equivilent\n",
        "to a row of the attention matrix, hence the name of the function.\n",
        "\"\"\"\n",
        "\n",
        "def compute_attention_row(s, hs, W=W_attention, U=U_attention, v=v_attention):\n",
        "    \"\"\"\n",
        "    computes alignments for all h values given s\n",
        "\n",
        "    s is a vector of length embedding size\n",
        "    hs is a tensor of shape (sequence length, embedding size)\n",
        "    the output is a vector of sequence length\n",
        "    \"\"\"\n",
        "    return F.softmax(torch.cat([alignment_func(s, h, W, U, v) for h in hs]),0)\n",
        "\n",
        "#testing the computation of an allignment row between the previous decoder\n",
        "#embedding and all encoder embeddings\n",
        "compute_attention_row(torch.rand(decoder_embedding_dim)/50, torch.rand(10,encoder_embedding_dim)/50)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "OmTDpCXx8be8",
        "outputId": "a024234b-f17c-4ea1-fd7c-c63f988ea41d"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "tensor([0.0541, 0.0790, 0.0875, 0.1084, 0.0649, 0.0490, 0.0944, 0.1974, 0.1529,\n",
              "        0.1124])"
            ]
          },
          "metadata": {},
          "execution_count": 11
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "\"\"\"\n",
        "Defining the attention module\n",
        "\n",
        "this will go into a larger network later. For now, we will create the module\n",
        "and train it on a synthetic dataset so we can confirm it can learn simple\n",
        "alignments.\n",
        "\"\"\"\n",
        "from torch import nn\n",
        "\n",
        "class Attention(nn.Module):\n",
        "    \"\"\"\n",
        "    -computes an alignment for all encoder embeddings\n",
        "    -constructs a context vector using those embeddings\n",
        "    -outputs that context vector\n",
        "    \"\"\"\n",
        "\n",
        "    def __init__(self, embed_dim=EMBED_DIM, encoder_embedding_dim=encoder_embedding_dim, decoder_embedding_dim=decoder_embedding_dim):\n",
        "        super(Attention, self).__init__()\n",
        "\n",
        "        #learnable attention parameters\n",
        "        self.U = nn.Parameter(torch.rand(embed_dim, encoder_embedding_dim), requires_grad=True )\n",
        "        self.W = nn.Parameter(torch.rand(embed_dim, decoder_embedding_dim), requires_grad=True )\n",
        "        self.v = nn.Parameter(torch.rand(1,embed_dim), requires_grad=True )\n",
        "        self.encoder_embedding_dim = encoder_embedding_dim\n",
        "\n",
        "        if torch.cuda.is_available():\n",
        "            self.cuda()\n",
        "\n",
        "    def forward(self, s, hn):\n",
        "        \"\"\"\n",
        "        computes a batch of context vectors given a current the all encoder\n",
        "        embeddings and the current decoder embedding\n",
        "        \"\"\"\n",
        "        #defining a tensor consisting of a context vector for each batch\n",
        "        context = torch.empty((s.shape[0], self.encoder_embedding_dim))\n",
        "\n",
        "        #iterating over all batches\n",
        "        for i, (s_single, hn_single) in enumerate(zip(s, hn)):\n",
        "            weights = compute_attention_row(s_single, hn_single, W=self.W, U=self.U, v=self.v)\n",
        "\n",
        "            #getting context for this batch\n",
        "            context_single = torch.sum(hn_single * weights[:, None], axis=0)\n",
        "\n",
        "            context[i] = context_single\n",
        "\n",
        "        return context\n",
        "\n",
        "print('==== Testing Attention ====')\n",
        "#testing if the attention mechanism can support different sizes\n",
        "test_attention = Attention()\n",
        "#getting the context of the first token of every batch. The token embedding\n",
        "#is of length 50, and and the batch size in this example is 3\n",
        "# test_attention(emb_fr[:,0], emb_en).shape\n",
        "test_attention(torch.rand(10,decoder_embedding_dim)/50, torch.rand(10,10,encoder_embedding_dim)/50).shape"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "5GxhdjQQm0Ee",
        "outputId": "112a59f9-04f4-45ee-902d-363dbfa57328"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "==== Testing Attention ====\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "torch.Size([10, 100])"
            ]
          },
          "metadata": {},
          "execution_count": 12
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Testing attention Mechanism\n",
        "I want to confirm the attention mechanism can generate reasonable alignments (instead of treating it like a black box and hoping it works). Because there's no positional encoding, and I don't have an encoder and decoder to give me embedded positional information, I have to use the values of `s` and `hn` to create some obvious association.\n",
        "\n",
        "Here's what I'll do:\n",
        "the input will have an embedding length of 2. The first number will be the current index, and the next value will be the next inde. I'll shuffle the input, and ask the model to correctly choose the output. So:\n",
        "\n",
        "The sequence will always start with an unshuffled `[0,1]`, and the model will be given the previous embedding (right or wrong) as `s`. This is to simulate how the decoder will probably have nonsensical output values for `s` initially.\n",
        "\n",
        "```\n",
        "[0,1],[5,6],[3,4],[1,2],[2,3] -> [0,1],[1,2],[2,3],[3,4],[5,6]\n",
        "```\n"
      ],
      "metadata": {
        "id": "R3ZBu4XG8mBK"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "print(torch.rand(10,decoder_embedding_dim).shape)\n",
        "print(torch.rand(10,10,encoder_embedding_dim).shape)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "riIzFA-_XJEM",
        "outputId": "1f1f2e42-2120-4037-c3db-23a2674118d9"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "torch.Size([10, 50])\n",
            "torch.Size([10, 10, 100])\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import random\n",
        "from tqdm import tqdm\n",
        "min_len = 5\n",
        "max_len = 20\n",
        "\n",
        "test_attention = Attention(10,2,2)\n",
        "loss_fn = nn.MSELoss()\n",
        "optimizer = torch.optim.SGD(test_attention.parameters(), lr=1e-2)\n",
        "lr_phase = 0\n",
        "\n",
        "#training on some number of random sequences\n",
        "batch_losses = []\n",
        "for i in tqdm(range(800)):\n",
        "\n",
        "    #generating x and y\n",
        "    y = []\n",
        "    x = []\n",
        "    for j in range(random.randint(min_len,max_len)):\n",
        "        y.append([j, j+1])\n",
        "        x.append([j,j+1])\n",
        "    random.shuffle(x)\n",
        "    x = np.expand_dims(np.array([[0,1]] + x), axis=0).astype(np.float32)\n",
        "    y = np.expand_dims(np.array([[0,1]] + y), axis=0).astype(np.float32)\n",
        "    x = torch.from_numpy(x)\n",
        "    y = torch.from_numpy(y)\n",
        "\n",
        "    #iterating over all training examples (given s predict s+1)\n",
        "    s_in = x[:,0]\n",
        "    sample_losses = []\n",
        "    for j in range(2,len(x[0])):\n",
        "\n",
        "        y_this = y[:,j]\n",
        "\n",
        "        optimizer.zero_grad()\n",
        "        s_out = test_attention(s_in, x)\n",
        "\n",
        "        loss = loss_fn(s_out, y_this)\n",
        "        sample_losses.append(loss.detach())\n",
        "\n",
        "        # debug printout\n",
        "        if False:\n",
        "            print('-------')\n",
        "            print('in: {}'.format(s_in))\n",
        "            print('out: {}'.format(s_out))\n",
        "            print('should be: {}'.format(y_this))\n",
        "            print('loss: {}'.format(loss))\n",
        "\n",
        "        loss.backward(retain_graph=True)\n",
        "        optimizer.step()\n",
        "\n",
        "        s_in = torch.clone(y_this).detach()\n",
        "\n",
        "    batch_loss = np.mean(sample_losses)\n",
        "    batch_losses.append(batch_loss)\n",
        "\n",
        "    #hacking together a simple learning rate scheduler\n",
        "    if batch_loss<0.05 and lr_phase == 0:\n",
        "        optimizer = torch.optim.SGD(test_attention.parameters(), lr=1e-3, momentum=0.2)\n",
        "        lr_phase+=1\n",
        "\n",
        "    #stopping training when loss is good enough\n",
        "    if batch_loss<0.03:\n",
        "        break\n",
        "\n",
        "plt.plot(batch_losses)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 465
        },
        "id": "yhLcW5m3-MkR",
        "outputId": "2f036622-1070-49ed-9af1-bd2b60c80b7f"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            " 84%|████████▍ | 673/800 [00:20<00:03, 32.90it/s]\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[<matplotlib.lines.Line2D at 0x7af99e684b20>]"
            ]
          },
          "metadata": {},
          "execution_count": 32
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "\"\"\"\n",
        "Visualizing alignment\n",
        "\"\"\"\n",
        "\n",
        "#generating x and y\n",
        "y = []\n",
        "x = []\n",
        "for j in range(1, random.randint(min_len,max_len)):\n",
        "    y.append([j, j+1])\n",
        "    x.append([j,j+1])\n",
        "random.shuffle(x)\n",
        "\n",
        "x = np.expand_dims(np.array([[0,1]] + x), axis=0).astype(np.float32)\n",
        "y = np.expand_dims(np.array([[0,1]] + y), axis=0).astype(np.float32)\n",
        "\n",
        "x = torch.from_numpy(x)\n",
        "y = torch.from_numpy(y)\n",
        "\n",
        "\n",
        "\n",
        "#Extracting learned parameters for generating alignment visual\n",
        "W = test_attention.W\n",
        "U = test_attention.U\n",
        "v = test_attention.v\n",
        "\n",
        "s = x[:,0]\n",
        "y_hat = []\n",
        "rows = []\n",
        "\n",
        "\n",
        "#predicting the next element in the sequence.\n",
        "#skipping over the trivia first, and not predicting one after the last.\n",
        "for _ in range(0,len(x[0])-2):\n",
        "\n",
        "    #computing attention weights for this output\n",
        "    row = list(compute_attention_row(s[0], x[0], W=W, U=U, v=v).detach().numpy())\n",
        "    rows.append(row)\n",
        "\n",
        "    #predicting what should be in this location.\n",
        "    with torch.no_grad():\n",
        "        s = torch.round(test_attention(s, x))\n",
        "\n",
        "    y_hat.append(list(s[0]))\n",
        "\n",
        "y_hat = np.array(y_hat)\n",
        "x_p = np.array(x)\n",
        "\n",
        "print('input: ')\n",
        "print(x_p)\n",
        "print('output: ')\n",
        "print(y_hat)\n",
        "\n",
        "from matplotlib.ticker import MaxNLocator\n",
        "alignments = np.array(rows)\n",
        "\n",
        "plt.pcolormesh(alignments, edgecolors='k', linewidth=2)\n",
        "ax = plt.gca()\n",
        "ax.set_aspect('equal')\n",
        "ax.yaxis.set_major_locator(MaxNLocator(integer=True))\n",
        "ax.xaxis.set_major_locator(MaxNLocator(integer=True))\n",
        "plt.title('Algnment scores used in attention')\n",
        "plt.ylabel('output index (each row is attention for an output)')\n",
        "plt.xlabel('input index')\n",
        "plt.show()"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "id": "NjpDAqUxlFln",
        "outputId": "3130c816-088b-4121-8194-5914f2b41e6b"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "input: \n",
            "[[[ 0.  1.]\n",
            "  [17. 18.]\n",
            "  [10. 11.]\n",
            "  [13. 14.]\n",
            "  [14. 15.]\n",
            "  [ 2.  3.]\n",
            "  [11. 12.]\n",
            "  [16. 17.]\n",
            "  [ 5.  6.]\n",
            "  [18. 19.]\n",
            "  [ 7.  8.]\n",
            "  [12. 13.]\n",
            "  [ 9. 10.]\n",
            "  [ 3.  4.]\n",
            "  [ 4.  5.]\n",
            "  [ 6.  7.]\n",
            "  [ 8.  9.]\n",
            "  [15. 16.]\n",
            "  [ 1.  2.]]]\n",
            "output: \n",
            "[[ 2.  3.]\n",
            " [ 3.  4.]\n",
            " [ 4.  5.]\n",
            " [ 5.  6.]\n",
            " [ 6.  7.]\n",
            " [ 7.  8.]\n",
            " [ 8.  9.]\n",
            " [ 9. 10.]\n",
            " [10. 11.]\n",
            " [11. 12.]\n",
            " [12. 13.]\n",
            " [13. 14.]\n",
            " [14. 15.]\n",
            " [15. 16.]\n",
            " [16. 17.]\n",
            " [17. 18.]\n",
            " [18. 19.]]\n"
          ]
        },
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ],
            "image/png": "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\n"
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Creating The Model\n",
        "This model uses a bi-directional LSTM as an encoder, a single LSTM as a decoder, and an attention mechanism between.\n",
        "\n",
        "For simplicity, the embeddings throughtout will have the same dimension. So the hidden state in the encoder, the attention mechanism, the hidden state in the decoder, all consistent. This was defined earlier as `EMBED_DIM`.\n",
        "\n",
        "The output dimension will have one more output: a boolean telling the model weather to stop outputing or not. I chose to add this chiefly because the embedding I'm using doesn't seem to have convenient support of utility tokens."
      ],
      "metadata": {
        "id": "knwyY0jpdPEj"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "\"\"\"\n",
        "Defining a bi-directional LSTM encoder\n",
        "\"\"\"\n",
        "import torch.nn as nn\n",
        "import numpy as np\n",
        "\n",
        "#defining the bi-directional encoder\n",
        "class Encoder(nn.Module):\n",
        "    def __init__(self):\n",
        "        super(Encoder, self).__init__()\n",
        "        self.bilstm = nn.LSTM(input_size=EMBED_DIM,\n",
        "                              hidden_size=EMBED_DIM,\n",
        "                              bidirectional=True)\n",
        "\n",
        "        if torch.cuda.is_available():\n",
        "            self.cuda()\n",
        "\n",
        "    def forward(self, x):\n",
        "        #outputting both layers of the bi-directional lstm as h\n",
        "        #this will have an embedding size which is twice as long\n",
        "        #which the attention mechanism needs to account for in the\n",
        "        #shape of the U matrix\n",
        "        out, (_, _) = self.bilstm(x)\n",
        "        return out\n",
        "\n",
        "\n",
        "print('==== Testing Bi-Directional LSTM encoder ====')\n",
        "test_encoder = Encoder()\n",
        "#adding a batch dimension\n",
        "print('input shape:')\n",
        "print(emb_en.shape)\n",
        "print('output shape:')\n",
        "encoder_output = test_encoder(emb_en)\n",
        "print(encoder_output.shape)\n",
        "\n"
      ],
      "metadata": {
        "id": "ygLmtul1Ve6I",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "05bccba4-bff0-45d2-90d8-43c2efb842a5"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "==== Testing Bi-Directional LSTM encoder ====\n",
            "input shape:\n",
            "torch.Size([3, 9, 50])\n",
            "output shape:\n",
            "torch.Size([3, 9, 100])\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "reference of sequence 2 sequence https://www.youtube.com/watch?v=EoGUlvhRYpk\n",
        "I can probably virtually completely vampire everything but the attention mechanism, which might be easier from a practical perspective."
      ],
      "metadata": {
        "id": "OkUJKv_gR_lF"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "\"\"\"\n",
        "Defining an LSTM Decoder\n",
        "\"\"\"\n",
        "\n",
        "#defining the bi-directional encoder\n",
        "class Decode(nn.Module):\n",
        "    def __init__(self, max_len = 100):\n",
        "        super(Encoder, self).__init__()\n",
        "        self.lstm = nn.LSTM(input_size=EMBED_DIM,\n",
        "                              hidden_size=EMBED_DIM,\n",
        "                              bidirectional=False)\n",
        "\n",
        "        self.max_len = max_len\n",
        "        if torch.cuda.is_available():\n",
        "            self.cuda()\n",
        "\n",
        "    def forward(self, x):\n",
        "\n",
        "        #Taking in the hidden state modifying, and outputting until end\n",
        "        for i in range(max_len):\n",
        "            out, (_, _) = self.lstm(x)\n",
        "        return out"
      ],
      "metadata": {
        "id": "9ZdOd4aaxqzK"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}